extern alias roslyn;
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.CodeDom.Compiler;

namespace ImmediateWindow
{
  public partial class MainForm : Form
  {
    List<string> theAvailableReferences;
    List<string> theDefaultReferences;
    Font lastFontSelected;
    Color lastForeColorSelected;
    MSILForm msilform;

    public MainForm()
    {
      InitializeComponent();
      msilform = new MSILForm();
      lastFontSelected = new Font("Courier New", 14);
      lastForeColorSelected = Color.Black;
      theDefaultReferences = new List<string>();
      theDefaultReferences.Add("System.dll");
      theAvailableReferences = LoadAvailableReferences();
      foreach (string reference in theDefaultReferences)
        theAvailableReferences.Remove(reference);
    }

    List<string> LoadAvailableReferences()
    {
      List<string> result = new List<string>();
      try
      {
        DirectoryInfo clrfolder = new DirectoryInfo(System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory());
        foreach (FileInfo assembly in clrfolder.GetFiles("*.dll"))
          result.Add(assembly.Name);
        return result;
      }
      catch
      {
        return result;
      }
    }

    public void StartMessagePumpInAnotherThread(object state)
    {
      WinFormRunner runner = state as WinFormRunner;
      runner.Invoke();
    }

    private void ExeButton_Click(object sender, EventArgs e)
    {
      GenerateAssembly(true, string.Empty);
    }

    CodeDomProvider csharp, jscript, visualbasic;
    CodeDomProvider CreateCompiler(DesignNotation language)
    {
      string compiler_version = ImmediateWindow.Properties.Settings.Default.CompilerVersion;
      switch (language)
      {
        case DesignNotation.CSharp:
          /*
          http://blogs.msdn.com/lukeh/archive/2007/07/11/c-3-0-and-codedom.aspx
          MSDN Library:CSharpCodeProvider Constructor (IDictionary<(Of <(String, String>)>))
          programatically:
            return csharp==null? csharp=new Microsoft.CSharp.CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } }) : csharp;

          by .config:
            return csharp == null ? csharp = CodeDomProvider.CreateProvider("csharp") : csharp;
          */
          if (string.IsNullOrEmpty(compiler_version))
            return csharp == null ? csharp = new roslyn::Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider() : csharp;
          else
            return csharp == null ? csharp = new roslyn::Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider() : csharp;

        case DesignNotation.CPPCLI:
          return new Microsoft.VisualC.CppCodeProvider();

        case DesignNotation.VisualBasic:
          if (string.IsNullOrEmpty(compiler_version))
            return visualbasic == null ? visualbasic = new Microsoft.VisualBasic.VBCodeProvider() : visualbasic;
          else
            return visualbasic == null ? visualbasic = new Microsoft.VisualBasic.VBCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", compiler_version } }) : visualbasic;

        case DesignNotation.JavaScript:
          return jscript == null ? jscript = new Microsoft.JScript.JScriptCodeProvider() : jscript;

        default:
          throw new Exception("Detailed design notation not supported:" + language);
      }
    }
    void GenerateAssembly(bool execute, string GeneratedName)
    {
      IInputOutputForm form = this.ActiveMdiChild as IInputOutputForm;
      form.WriteOutput("");
      StringBuilder codelines = new StringBuilder();
      try
      {
        CodeDomProvider provider = CreateCompiler(form.CompilerOption.Language);
        CompilerParameters options = new CompilerParameters();
        options.GenerateExecutable = true;
        options.IncludeDebugInformation = false;
        foreach (string assemblyname in form.References)
          options.ReferencedAssemblies.Add(assemblyname);
        if (string.IsNullOrEmpty(GeneratedName))
          options.GenerateInMemory = true;
        else
        {
          options.GenerateInMemory = false;
          options.OutputAssembly = GeneratedName;
        }
        options.WarningLevel = form.CompilerOption.WarningLevel;
        options.TreatWarningsAsErrors = form.CompilerOption.WarningsAsErrors;
        options.CompilerOptions = form.CompilerOption.GetAllCompilerSwitches();
        form.WriteOutput(string.Format("{0} {1} CLR:{2}:{3}", form.CompilerOption.Language,Properties.Settings.Default.CompilerVersion, Environment.Version, options.CompilerOptions));
        options.TempFiles = new TempFileCollection(".", false);
        //if (provider.Supports(GeneratorSupport.EntryPointMethod))
        //  options.MainClass = "exe";
        string[] sources = form.GetSourceLines();
        StatusLabel.Text = "Compiling...";
        CompilerResults results = provider.CompileAssemblyFromSource(options, sources);
        if (results.NativeCompilerReturnValue == 0)
        {
          ApplicationType apptype = form.CompilerOption.ApplicationType;
          if (!execute || apptype == ApplicationType.library || apptype == ApplicationType.module)
          {
            StatusLabel.Text = "Compiled.";
            return;
          }
          StatusLabel.Text = "Executing...";
          Assembly exe = results.CompiledAssembly;

          switch (form.CompilerOption.ApplicationType)
          {
            case ApplicationType.exe:
              {
                StringWriter stringer = new StringWriter();
                Console.SetOut(stringer);
                ParameterInfo[] pars = exe.EntryPoint.GetParameters();
                exe.EntryPoint.Invoke(null, pars.Length > 0 ? form.GetArguments() : null);
                StatusLabel.Text = "Executed.";
                form.WriteOutput(stringer.GetStringBuilder().ToString());
                break;
              }
            case ApplicationType.winexe:
              {
                ParameterInfo[] pars = exe.EntryPoint.GetParameters();
                WinFormRunner runner = new WinFormRunner(exe, pars.Length > 0 ? form.GetArguments() : null);
                System.Threading.ThreadPool.QueueUserWorkItem(StartMessagePumpInAnotherThread, runner);
                StatusLabel.Text = "Queued.";
                break;
              }
          }
        }
        else
        {
          codelines.Length = 0;
          foreach (CompilerError err in results.Errors)
            codelines.AppendFormat("({0},{1}) {2}:{3} / ", err.Line, err.Column, err.ErrorNumber, err.ErrorText);
          form.WriteOutput(codelines.ToString());
        }
      }
      catch (Exception err)
      {
        StatusLabel.Text = "Exception";
        MessageBox.Show("Exception:" + err);
      }
    }

    private void ToggleBorderButton_Click(object sender, EventArgs e)
    {
      if (this.FormBorderStyle == FormBorderStyle.None)
        this.FormBorderStyle = FormBorderStyle.Sizable;
      else
        this.FormBorderStyle = FormBorderStyle.None;
    }

    private void QuitButton_Click(object sender, EventArgs e)
    {
      Close();
    }

    void AddImmediate(Form newform)
    {
      if (!ExeButton.Enabled)
      {
        ExeButton.Enabled = true;
        CompileButton.Enabled = true;
        ILDASMButton.Enabled = true;
        RefsButton.Enabled = true;
        OptionsButton.Enabled = true;
        ClearOutButton.Enabled = true;
      }
      IInputOutputForm form = newform as IInputOutputForm;
      form.References = theDefaultReferences;
      form.AvailableReferences = theAvailableReferences;
      form.CompilerOption = new CompilerKnobs(DesignNotation.CSharp, 4, true, "/optimize+ /checked+ /nostdlib-", ApplicationType.exe);
      if (lastFontSelected != null)
        foreach (Control ctrl in form.GetFontControls())
        {
          ctrl.Font = lastFontSelected;
          ctrl.ForeColor = lastForeColorSelected;
        }

      newform.MdiParent = this;
      Utility.AdjustSize(newform);
      int newindex = ActivesListBox.Items.Add(new ActiveImmediate(newform));
      ActivesListBox.SelectedIndex = newindex;
      newform.Show();
    }

    private void ConsoleLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
      AddImmediate(new ConsoleForm());
    }

    private void ExprLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
      AddImmediate(new ImmediateExprForm());
    }

    private void Template1Label_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
      AddImmediate(new BlankCompilationUnitForm(null));
    }

    private void BlankPreLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
      string[] prelines ={"using System;",
			"",
			"class exe",
			"{",
			"\tstatic void Main(string[] args)",
			"\t{",
			"\t}",
			"}"};
      AddImmediate(new BlankCompilationUnitForm(prelines));
    }

    private void TryCatchLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
    {
      string[] prelines ={"using System;",
      "",
			"class exe",
			"{",
			"\tstatic void Main(string[] args)",
			"\t{",
      "\t\ttry",
      "\t\t{",
      "\t\t}",
      "\t\tcatch(Exception ex)",
      "\t\t{",
      "\t\t\tConsole.WriteLine(\"{0}:{1}\",ex.GetType().FullName,ex.Message);",
      "\t\t}",
			"\t}",
			"}"};
      AddImmediate(new BlankCompilationUnitForm(prelines));
    }

    private void MainForm_Layout(object sender, LayoutEventArgs e)
    {
      Utility.AdjustSize(this.ActiveMdiChild);
    }

    private void ActivesListBox_SelectedIndexChanged(object sender, EventArgs e)
    {
      ActiveImmediate newselection = ActivesListBox.SelectedItem as ActiveImmediate;
      newselection.itsForm.BringToFront();
    }

    private void CompileButton_Click(object sender, EventArgs e)
    {
      GenerateAssembly(false, string.Empty);
    }

    private void RefsButton_Click(object sender, EventArgs e)
    {
      IInputOutputForm form = this.ActiveMdiChild as IInputOutputForm;
      using (ReferenceForm refs = new ReferenceForm(form.References, form.AvailableReferences))
      {
        refs.ShowDialog();
      }
    }

    private void OptionsButton_Click(object sender, EventArgs e)
    {
      IInputOutputForm form = this.ActiveMdiChild as IInputOutputForm;
      Control[] controls = form.GetFontControls();
      using (OptionsForm options = new OptionsForm(form.CompilerOption, controls))
      {
        if (options.ShowDialog() == DialogResult.OK && controls.Length > 0)
        {
          lastFontSelected = controls[0].Font;
          lastForeColorSelected = controls[0].ForeColor;
        }
      }
    }

    private void ClearOutButton_Click(object sender, EventArgs e)
    {
      IInputOutputForm form = this.ActiveMdiChild as IInputOutputForm;
      form.ClearOutput();
    }

    private void MainForm_KeyDown(object sender, KeyEventArgs e)
    {
      if (e.KeyCode == Keys.Escape)
        Close();
    }

    private void ILDASMButton_Click(object sender, EventArgs e)
    {
      ActiveImmediate newselection = ActivesListBox.SelectedItem as ActiveImmediate;
      string ILDasmID = newselection.ToString();
      string assembly_name = Utility.NormalizeToFileName(ILDasmID);
      string full_assembly_name = Environment.CurrentDirectory + "\\" + assembly_name;
      GenerateAssembly(false, assembly_name);
      if (!File.Exists(full_assembly_name))
      {
        StatusLabel.Text = "NO AssemblyFile";
        return;
      }
      string MSIL_lines = Utility.Disassemble(assembly_name);
      if (string.IsNullOrEmpty(MSIL_lines))
      {
        StatusLabel.Text = "NO ILDASM.";
        return;
      }

      msilform.Text = ILDasmID;
      msilform.MSILTextBox.Font = lastFontSelected;
      msilform.MSILTextBox.ForeColor = lastForeColorSelected;
      msilform.MSILTextBox.Text = MSIL_lines;
      int width = ClientRectangle.Width - CommonBarPanel.ClientRectangle.Width;
      int height = ClientRectangle.Height - 20;
      msilform.Location = new Point(width / 2, 10);
      msilform.Size = new Size(width / 2, height - 20);
      msilform.Show();

      StatusLabel.Text = "Disassembled.";
      File.Delete(full_assembly_name);
    }
  }
}